﻿using System;
using System.Collections.Generic;
 
using System.Text;
using System.Reflection;
using System.IO;
using System.Data.SqlClient;
using System.Data;
using System.Data.SqlTypes;
using DataProvider.DBAttribute;
using System.Xml;
using System.Data.Common;
namespace DataProvider
{
    [Serializable]
    public abstract class BaseEntity:IDBAccess
    {
        [DBField("ID")]
        public String ID = "";

        //[DBField("CreateDate", true)]
        //public String CreateDate = "";

        private static Dictionary<String, String> identitySqlCache = new Dictionary<string, String>(StringComparer.OrdinalIgnoreCase);
        private static Dictionary<String, List<FieldInfo>> selectFieldCache = new Dictionary<string, List<FieldInfo>>(StringComparer.OrdinalIgnoreCase);
        private static Dictionary<String, Type> entityTypeCache = new Dictionary<string, Type>();
        private static Dictionary<String, PrimaryKeyEntity> keyCache = new Dictionary<string, PrimaryKeyEntity>();
        private static Dictionary<String, List<String>> mapFieldColCache = new Dictionary<string, List<String>>(StringComparer.OrdinalIgnoreCase);

        public bool HasData = false;

        public String CatchKey = "";

        public bool IsNew = true;
        public bool Checked = false;
        private bool isLoad = false;

        private String primaryKey = "ID";

        public String PrimaryKey
        {
            get
            {
                return this.primaryKey;
            }
            set
            {
                if (String.IsNullOrEmpty(value))
                    return;
                this.primaryKey = value;
                this.idIdentityGroup[0] = this.primaryKey;
            }
        }

        private String primaryKeyType= " varchar(66)";

        public String PrimaryKeyType
        {
            get{
                return this.primaryKeyType;
            }
            set
            {
                if (String.IsNullOrEmpty(value))
                    return;
                this.primaryKeyType = value;
            }
        }

        private bool hasCustPrimaryKey
        {
            get
            {
                if (String.Compare("ID", this.PrimaryKey, true) != 0 || 
                    String.Compare("varchar(66)", primaryKeyType.Trim(), true) != 0)
                {
                    return true;
                }
                return false;
            }
        }

        public bool IDAutoIncrease = false;

        public List<String> UnUpdateFields
        {
            get
            {
                return this.unUpdateFields;
            }
        }
        private List<String> unUpdateFields = new List<string>();

        public List<String> UpdateFields
        {
            get
            {
                return this.updateFields;
            }
        }
        private List<String> updateFields = new List<string>();

        public List<String> UnInsertFields
        {
            get
            {
                return this.unInsertField;
            }
        }
        private List<String> unInsertField = new List<string>();

        public List<String> InsertFields
        {
            get
            {
                return this.insertFields;
            }
        }
        private List<String> insertFields = new List<string>();

        private static Type[] entityTypes = null;
        private static SqlConfigFile sqlConfig = new SqlConfigFile();

        private String[] idIdentityGroup = new String[] { "ID"};


        public List<String[]> IdentityFieldSet
        {
            get { return identityFieldSet; }
        }
        private List<String[]> identityFieldSet = new List<String[]>();


        public List<String> UnSelectFields
        {
            get
            {
                return this.unSelectFields;
            }
        }
        private List<String> unSelectFields = new List<string>();

        public List<String> SelectFields
        {
            get
            {
                return this.selectFields;
            }
        }
        private List<String> selectFields = new List<string>();

        ////////////////////////////////////////////////////////////////
        private static Dictionary<String, List<FieldInfo>> entityFieldCache = new Dictionary<string, List<FieldInfo>>();
        private List<FieldInfo> GetEntityTableFileds(bool forSelect)
        {
            Type type = this.GetType();

            String key = type.FullName +"-"+ forSelect.ToString();

            if (keyCache.ContainsKey(key))
            {
                PrimaryKeyEntity keyEntity = keyCache[key];
                this.idIdentityGroup[0] = keyEntity.PrimaryKey;
                this.PrimaryKey = keyEntity.PrimaryKey;
                this.PrimaryKeyType = keyEntity.PrimaryKeyType;
                this.IDAutoIncrease = keyEntity.IDAutoIncrease;
            }
            else
            {
                this.idIdentityGroup[0] = "ID";
                this.PrimaryKey = "ID";
                this.PrimaryKeyType = "varchar(66)";
                this.IDAutoIncrease = false;
            }
            if (entityFieldCache.ContainsKey(key))
            {
                return entityFieldCache[key];
            }


            List<FieldInfo> tableFieldInfoSet = new List<FieldInfo>();
            List<FieldInfo> entityFields = new List<FieldInfo>();
            //////////////////////////////////////////////////////////////////////////////
            FieldInfo[] fieldInfos = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetProperty);

            tableFieldInfoSet.AddRange(fieldInfos);
            //////////////////////////////////////////////////////////////////////////////
            foreach (FieldInfo fieldInfo in tableFieldInfoSet)
            {
                foreach (Attribute attr in fieldInfo.GetCustomAttributes(false))
                {
                    DBField fieldA = attr as DBField;
                    if (fieldA == null)
                    {
                        continue;
                    }
                    if (forSelect)
                    {
                        if (!entityFields.Contains(fieldInfo))
                        {
                            entityFields.Add(fieldInfo);
                        }
                    }
                    else if (!fieldA.OnlySelect)
                    {
                        if (!entityFields.Contains(fieldInfo))
                        {
                            entityFields.Add(fieldInfo);
                        }
                    }
                    if (fieldA.IsPrimaryKey)
                    {
                        PrimaryKeyEntity keyEntity = new PrimaryKeyEntity();
                        keyEntity.PrimaryKey = fieldA.FiledName;
                        keyEntity.PrimaryKeyType = (fieldA.FieldType == DBFieldType.Varchar ? " varchar(66)" : "int");
                        keyEntity.IDAutoIncrease = fieldA.IdAutoIncrease;

                        this.PrimaryKey = keyEntity.PrimaryKey;
                        this.PrimaryKeyType = keyEntity.PrimaryKeyType;
                        this.IDAutoIncrease = keyEntity.IDAutoIncrease;

                        //Set
                        this.idIdentityGroup[0] = this.PrimaryKey;
                        //Save in cache
                        keyCache[key] = keyEntity;
                    }

                }
            }//end for
            entityFieldCache[key] = entityFields;
            return entityFields;
        }

        private List<FieldInfo> GetEntitySelectedFields()
        {
            List<FieldInfo> vaildFields = new List<FieldInfo>();
            List<FieldInfo> entityFields = GetEntityTableFileds(true);
            foreach (FieldInfo fieldInfo in entityFields)
            {

                if (String.Compare(this.PrimaryKey, fieldInfo.Name, true) == 0 )
                {
                    if (this.hasCustPrimaryKey && String.Compare(fieldInfo.Name,"ID",true)==0)
                        continue;
                  
                    if (!vaildFields.Contains(fieldInfo))
                    {
                        vaildFields.Add(fieldInfo);
                    }
                }
                if (StringUtils.ContainStringInList(unSelectFields,fieldInfo.Name,true))
                {
                    continue;
                }
                if (this.selectFields.Count == 0)
                {
                    if (!vaildFields.Contains(fieldInfo))
                    {
                        vaildFields.Add(fieldInfo);
                    }
                }
                else
                {
                    if (StringUtils.ContainStringInList(selectFields,fieldInfo.Name,true))
                    {
                        if (!vaildFields.Contains(fieldInfo))
                        {
                            vaildFields.Add(fieldInfo);
                        }
                    }
                }
            }
            //////////////////////////////////////////////////////
            return vaildFields; 
        }

        private List<FieldInfo> GetEntityUpdateFields()
        {
            List<FieldInfo> vaildFields = new List<FieldInfo>();
            List<FieldInfo> entityFields = GetEntityTableFileds(false);
            foreach (FieldInfo fieldInfo in entityFields)
            {
                if (String.Compare(this.PrimaryKey, fieldInfo.Name, true) == 0)
                {
                    continue;
                }
                if (this.unUpdateFields.Count == 0 || 
                    (!StringUtils.ContainStringInList(this.unUpdateFields,fieldInfo.Name,true)))
                {
                    if (this.updateFields.Count == 0 ||
                        StringUtils.ContainStringInList(this.updateFields,fieldInfo.Name,true))
                    {
                        if (!vaildFields.Contains(fieldInfo))
                        {
                            vaildFields.Add(fieldInfo);
                        }
                    }
                }
                 
            }
            //////////////////////////////////////////////////////
            return vaildFields; 
        }

        private List<FieldInfo> GetEntityInsertFields()
        {
            List<FieldInfo> vaildFields = new List<FieldInfo>();
            List<FieldInfo> entityFields = GetEntityTableFileds(false);
            foreach (FieldInfo fieldInfo in entityFields)
            {
                if (String.Compare(this.PrimaryKey, fieldInfo.Name, true) == 0)
                {
                   
                    if (this.IDAutoIncrease)
                    {
                        continue;
                    }
                    if (!vaildFields.Contains(fieldInfo))
                    {
                        vaildFields.Add(fieldInfo);
                    }
                }
                if (this.unInsertField.Count == 0 ||
                    !StringUtils.ContainStringInList(this.unInsertField,fieldInfo.Name,true))
                {
                    if (this.insertFields.Count == 0 || 
                      StringUtils.ContainStringInList(this.insertFields,fieldInfo.Name,true))
                    {
                        if (!vaildFields.Contains(fieldInfo))
                        {
                            vaildFields.Add(fieldInfo);
                        }
                    }
                }
            }
            //////////////////////////////////////////////////////
            return vaildFields;

        }
        ////////////////////////////////////////////////////////////////

        private static void LoadSqlXml( )
        {
            //foreach (String dataEntityString in Config.EntityList)
            //{
            //    String[] entityParts = dataEntityString.Split(new String[] {":" }, StringSplitOptions.RemoveEmptyEntries);
            //    if (entityParts.Length != 2)
            //        continue;
            //    String xml = RsUtils.GetResourceString(entityParts[0], entityParts[1]);
            //    if (String.IsNullOrEmpty(xml))
            //        throw new Exception("装载:" + entityParts[1] +"失败，请检查配置文件");
            //    sqlConfig.LoadXml(xml);
            //}
        }

        private static void LoadEntityType()
        {
            List<Type> dllTypeList = new List<Type>();
            foreach (String dataEntityString in Config.EntityList)
            {
                String[] entityParts = dataEntityString.Split(new String[] { ":" }, StringSplitOptions.RemoveEmptyEntries);
                if (entityParts.Length != 2)
                    continue;
                Assembly assembly = RsUtils.LoadAssemblyFromFile(entityParts[0]);
                dllTypeList.AddRange(assembly.GetTypes());
            }
            entityTypes = dllTypeList.ToArray();
        }

        public BaseEntity()
        {
            this.ID = NewId();
            this.identityFieldSet.Add(idIdentityGroup);
            this.IsNew = true;
        }

        static BaseEntity()
        {
            try
            {
                LoadSqlXml();
                LoadEntityType();
            }
            catch(Exception e)
            {
                DataLayerLog.LogError(e.Message);
                //throw e;
            }
        }

        ///////////////////////////////////////////////////////////////
        public T Clone<T>() where T : BaseEntity,new()
        {
            T newObj = new T();

            ////////////////////////////////////////////////
            List<FieldInfo> entityFields = null;
            String key = this.GetType().FullName;
            if (entityFieldCache.ContainsKey(key))
            {
                entityFields = entityFieldCache[key];
            }
            else
            {
                entityFields = this.GetEntityTableFileds(false);
                entityFieldCache[key] = entityFields;
            }
            ////////////////////////////////////////////////
            foreach (FieldInfo field in entityFields)
            {
                Object value = field.GetValue(this);
                field.SetValue(newObj, value);
            }
            return newObj;
        }

        private DbParameter[] GetEntitySqlParameter(List<FieldInfo> selectedFileds)
        {
            List<SqlParameter> paraSet = new List<SqlParameter>();
            List<String> paraNameList = new List<string>();
            foreach (FieldInfo fieldInfo in selectedFileds)
            {
                String paraName = "@" + fieldInfo.Name.ToUpper();
                if (paraNameList.Contains(paraName.ToUpper()))
                {
                    continue;
                }
                SqlParameter para = null;
                if (String.Compare("CreateDate", fieldInfo.Name, true) == 0)
                {
                    para = new SqlParameter("@CREATEDATE", DateTime.Now.ToString());
                }
                else
                {
                    Object paraValue = fieldInfo.GetValue(this);
                    SqlDbType dbType = SqlUtils.GetSqlDataType(fieldInfo.FieldType, paraValue == null ? 2 : paraValue.ToString().Length);
                    para = SqlUtils.CreateSqlParameter(paraName, dbType, paraValue);
                    if (fieldInfo.FieldType == typeof(decimal) && paraValue != null && paraValue.ToString().Length >0)
                    {
                        decimal dValue;
                        decimal.TryParse(paraValue.ToString(), out dValue);
                        para.Value = dValue;
                    }
                }
                if (!paraSet.Contains(para))
                {
                    paraSet.Add(para);
                }
                paraNameList.Add(paraName.ToUpper());
            }
            ////////////加上ID,CreateDate/////////////////////////////
            SqlParameter idPara = new SqlParameter("@" + this.PrimaryKey.ToUpper(), this.ID);
            SqlParameter ctPara = new SqlParameter("@CREATEDATE", DateTime.Now.ToString());
            if (!IsExistPara(paraSet,idPara))
            {
                paraSet.Add(idPara);
            }
            if (!IsExistPara(paraSet, ctPara))
            {
                paraSet.Add(ctPara);
            }
           
            return paraSet.ToArray();
        }
        private bool IsExistPara(List<SqlParameter> paraSet,SqlParameter para)
        {
            if (paraSet == null || paraSet.Count == 0) return false;
            foreach (SqlParameter paraItem in paraSet)
            {
                if (String.Compare(paraItem.ParameterName, para.ParameterName, true) == 0)
                    return true;
            }
            return false;
        }
        private class CmpParameter:IEqualityComparer<SqlParameter>
        {
            public bool Equals(SqlParameter x, SqlParameter y)
            {
                return String.Compare(x.ParameterName, y.ParameterName, true) == 0;
            }
 
            public int GetHashCode(SqlParameter obj)
            {
                return (int)DateTime.Now.Ticks / 100 + 20;
            }
        }
        

        public DbParameter[] GetEntitySqlParameter(params String[] paras)
        {
            List<FieldInfo> entityFields = GetEntityTableFileds(true);
            if (paras == null || paras.Length == 0)
                return GetEntitySqlParameter(entityFields);

            List<String> paraList = new List<string>(paras);
            List<FieldInfo> vaildFields = new List<FieldInfo>();
            foreach (FieldInfo fieldInfo in entityFields)
            {
                if (String.Compare(this.PrimaryKey, fieldInfo.Name, true) == 0)
                {
                    if (!vaildFields.Contains(fieldInfo))
                    {
                        vaildFields.Add(fieldInfo);
                    }
                }
                if (StringUtils.ContainStringInList(paraList,fieldInfo.Name, true))
                {
                    if (!vaildFields.Contains(fieldInfo))
                    {
                        vaildFields.Add(fieldInfo);
                    }
                }
            }
            //////////////////////////////////////////////////////
            return GetEntitySqlParameter(vaildFields); ; 
        }

        ////////////////////////////////////////////////////////////////////
        private List<FieldInfo> GetSeletedFieldForMap()
        {
            List<FieldInfo> selectedFileds = null;
            if (String.IsNullOrEmpty(CatchKey) || !selectFieldCache.ContainsKey(CatchKey))
            {
                selectedFileds = this.GetEntitySelectedFields();
                if (!String.IsNullOrEmpty(CatchKey))
                {
                    selectFieldCache[this.CatchKey] = selectedFileds;
                }
            }
            else
            {
                selectedFileds = selectFieldCache[this.CatchKey];
            }
            return selectedFileds;
        }

        public static void CleanSelectCache(String cacheKey)
        {
            if (selectFieldCache.ContainsKey(cacheKey))
            {
                selectFieldCache.Remove(cacheKey);
            }
        }

        public void MapEntity(DbDataReader reader)
        {
            if (reader == null)
                return;
            try
            {
                //////////////////////////////////////////////////////////////////////////////
                List<String> readerColum = null;
                if (!String.IsNullOrEmpty(CatchKey))
                {
                    if (!mapFieldColCache.TryGetValue(CatchKey, out readerColum))
                    {
                        readerColum = new List<string>();
                        for (int i = 0; i < reader.FieldCount; i++)
                        {
                            readerColum.Add(reader.GetName(i).ToUpper());
                        }
                        mapFieldColCache[CatchKey] = readerColum;
                    }
                }
                else
                {
                    readerColum = new List<string>();
                    for (int i = 0; i < reader.FieldCount; i++)
                    {
                        readerColum.Add(reader.GetName(i).ToUpper());
                    }
                    mapFieldColCache[CatchKey] = readerColum;
                }
                //////////////////////////////////////////////////////////////////////////////
                List<FieldInfo> selectedFileds = GetSeletedFieldForMap();
                foreach (FieldInfo fieldInfo in selectedFileds)
                {
                    if (readerColum.Contains(fieldInfo.Name.ToUpper()))
                    {
                        Object value = reader[fieldInfo.Name];
                        InsertValueToFieldInfo(fieldInfo, value);
                    }
                }
                this.isLoad = true;
                this.IsNew = false;
                this.HasData = true;
                this.selectFields.Clear();
            }
            catch(Exception ef)
            {
                mapFieldColCache = new Dictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase);
                DataLayerLog.LogException(ef);
            }
        }
 
        public void MapEntity(DataRow row)
        {
            if (row == null)
                return;
            try
            {
                List<FieldInfo> selectedFileds = GetSeletedFieldForMap();
                //////////////////////////////////////////////////////////////////////////////
                List<String> readerColum = null;
                if (!String.IsNullOrEmpty(CatchKey))
                {
                    if (!mapFieldColCache.TryGetValue(CatchKey, out readerColum))
                    {
                        readerColum = new List<string>();
                        for (int i = 0; i < row.Table.Columns.Count; i++)
                        {
                            readerColum.Add(row.Table.Columns[i].ColumnName);
                        }
                        mapFieldColCache[CatchKey] = readerColum;
                    }
                }
                else
                {
                    readerColum = new List<string>();
                    for (int i = 0; i < row.Table.Columns.Count; i++)
                    {
                        readerColum.Add(row.Table.Columns[i].ColumnName);
                    }
                    mapFieldColCache[CatchKey] = readerColum;
                }
                //////////////////////////////////////////////////////////////////////////////
                foreach (FieldInfo fieldInfo in selectedFileds)
                {
                    Object value = row[fieldInfo.Name];
                    InsertValueToFieldInfo(fieldInfo, value);
                }
                this.isLoad = true;
                this.IsNew = false;
                this.HasData = true;
                this.selectFields.Clear();
            }
            catch (Exception ef)
            {
                mapFieldColCache = new Dictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase);
                DataLayerLog.LogException(ef);
            }
        }

        private void InsertValueToFieldInfo(FieldInfo fieldInfo, Object value)
        {
            if (fieldInfo.FieldType == typeof(String))
            {
                Object orgValue = fieldInfo.GetValue(this);
                String fValue = (orgValue == null ? "" : orgValue.ToString());
                String newValue = (value == null ? "" : value.ToString().Trim());

                if (!String.IsNullOrEmpty(fValue) 
                    && String.Compare(fValue, newValue, true) != 0
                    && isLoad 
                    &&fValue.Length > 2000)
                {
                    fValue = fValue + newValue;
                }
                else
                {
                    fValue = newValue;
                }
                fieldInfo.SetValue(this, fValue);
            }
            else if (fieldInfo.FieldType == typeof(Int32) ||
                     fieldInfo.FieldType == typeof(Int64) ||
                     fieldInfo.FieldType == typeof(Int16) ||
                     fieldInfo.FieldType == typeof(long))
            {
                if (value != null)
                {
                    int intValue;
                    int.TryParse(value.ToString().Trim(), out intValue);
                    fieldInfo.SetValue(this, intValue);
                }
                else
                {
                    fieldInfo.SetValue(this, 0);
                }
            }
            else if (fieldInfo.FieldType == typeof(byte[]))
            {
                byte[] data = value as byte[];
                fieldInfo.SetValue(this, data);
            }
            else if (fieldInfo.FieldType == typeof(double))
            {
                double dValue;
                double.TryParse(value.ToString(), out dValue);
                fieldInfo.SetValue(this, dValue);
            }
            else if (fieldInfo.FieldType == typeof(float))
            {
                float dValue;
                float.TryParse(value.ToString(), out dValue);
                fieldInfo.SetValue(this, dValue);
            }
            else if (fieldInfo.FieldType == typeof(decimal))
            {
                if (value == null)
                    value = "0";
                decimal dValue;
                decimal.TryParse(value.ToString(), out dValue);
                fieldInfo.SetValue(this, dValue);
            }
            else
            {
                fieldInfo.SetValue(this, value);
            }
        }

        ////////////////////////////////////////////////////////////////////

        public String GetIndetitySql()
        {
            bool hasIndentityField = false;
            String ckKey = this.GetType().FullName;
            if (identitySqlCache.ContainsKey(ckKey))
            {
                return identitySqlCache[ckKey];
            }

            StringBuilder sql = new StringBuilder();
            for (int i = 0; i < this.identityFieldSet.Count;i++ )
            {
                String[] fieldList = this.identityFieldSet[i];
                if (fieldList != null && fieldList.Length > 0)
                {
                    if (i == 0)
                    {
                        sql.Append("(");
                    }
                    else
                    {
                        sql.Append(" OR (");
                    }
                    //////////////////////////////////////////////////
                    for (int j = 0; j < fieldList.Length; j++)
                    {
                        String field = fieldList[j];
                        if (j == 0)
                        {
                            hasIndentityField = true;
                            sql.AppendFormat(" ({0} IS NOT NULL AND {0}=@{0}) ", field);
                        }
                        else
                        {
                            sql.AppendFormat(" And ({0} IS NOT NULL AND {0}=@{0}) ", field);
                        }
                    }
                    //////////////////////////////////////////////////
                    sql.Append(")");
                }
            }
            String sqlValue = sql.ToString();
            if (!hasIndentityField)
            {
                sqlValue = String.Format(" {0}=@{0} ", PrimaryKey);
            }
            else
            {
                sqlValue += String.Format(" OR ({0}=@{0}) ", PrimaryKey);
            }
            identitySqlCache[ckKey] = sqlValue;
            return sqlValue;
        }

        private String GetSqlParaByName(String fieldName)
        {
            FieldInfo fieldInfo = this.GetType().GetField(fieldName,BindingFlags.Public|BindingFlags.Instance);
            Object fObj = fieldInfo.GetValue(this);
            if (fObj == null)
                fObj = "";
            SqlDbType dbType = SqlUtils.GetSqlDataType(fieldInfo.GetType(), 10);
            return SqlUtils.CreateSqlValue(fObj.ToString(), dbType);
        }
        private String GetIndetitySqlWithoutParameter()
        {
            bool hasIndentityField = false;
            StringBuilder sql = new StringBuilder();
            for (int i = 0; i < this.identityFieldSet.Count; i++)
            {
                String[] fieldList = this.identityFieldSet[i];
                if (fieldList != null && fieldList.Length > 0)
                {
                    if (i == 0)
                    {
                        sql.Append("(");
                    }
                    else
                    {
                        sql.Append(" OR (");
                    }
                    //////////////////////////////////////////////////
                    for (int j = 0; j < fieldList.Length; j++)
                    {
                        String field = fieldList[j];
                        if (j == 0)
                        {
                            hasIndentityField = true;
                            sql.AppendFormat(" ({0} IS NOT NULL AND {0}={1}) ", field, GetSqlParaByName(field));
                        }
                        else
                        {
                            sql.AppendFormat(" And ({0} IS NOT NULL AND {0}={1}) ", field, GetSqlParaByName(field));
                        }
                    }
                    //////////////////////////////////////////////////
                    sql.Append(")");
                }
            }
            String sqlValue = sql.ToString();
            if (!hasIndentityField)
            {
                sqlValue = String.Format(" {0}={1} ", PrimaryKey,GetSqlParaByName(PrimaryKey));
            }
            else
            {
                sqlValue += String.Format(" OR ({0}={1}) ", PrimaryKey, GetSqlParaByName(PrimaryKey));
            }
            return sqlValue;
        }


        public String GetSaveSql(bool insert,bool update)
        {
            if (!insert && !update)
                throw new Exception("Insert Or Update 必须有一个为真");

            String key =this.GetType().FullName;
            if (sqlConfig.EntitySqlConfigCollection.ContainsKey(key) 
                && this.insertFields.Count == 0 
                && this.unInsertField.Count ==0 &&
                !String.IsNullOrEmpty(sqlConfig.EntitySqlConfigCollection[key].InsertSql.Trim()))
            {
                return sqlConfig.EntitySqlConfigCollection[key].InsertSql;
            }
            const String entitySqlTemplate = @"DECLARE @EID {0}
                                               SELECT @EID={1} FROM {2} WHERE {3}
                                               IF @@ROWCOUNT >0
                                               BEGIN 
                                                 [_Update_]
                                               END
                                               ELSE
                                               BEGIN
                                                 [_Insert_]
                                               END";

            String tableName = this.GetType().Name;
            String identitySql = GetIndetitySql();
            String sql= String.Format(entitySqlTemplate, 
                PrimaryKeyType,
                PrimaryKey,
                tableName, 
                identitySql);
            if (insert)
            {
                sql = sql.Replace("[_Insert_]", this.GetInsertSql());
            }
            else
            {
                sql = sql.Replace("[_Insert_]", "Select @EID");
            }
            if (update)
            {
                sql = sql.Replace("[_Update_]", this.GetUpdateSql(false));
            }
            else
            {
                sql = sql.Replace("[_Update_]", "Select @EID");
            }
            return sql;
        }

        public String GetInsertSql()
        {
            const String entitySqlTemplate = @" Insert into {0}({1})
                                                SELECT {2}

                                                SELECT @{3}";
            String tableName = this.GetType().Name;

            StringBuilder fieldString = new StringBuilder();
            StringBuilder paraString = new StringBuilder();
            List<FieldInfo> selectedFileds = this.GetEntityInsertFields();

            foreach (FieldInfo fieldInfo in selectedFileds)
            {
                String fieldName = fieldInfo.Name;
                fieldString.Append(fieldName);
                fieldString.Append(",");

                paraString.Append("@" + fieldName);
                paraString.Append(",");
            }
            String fieldStringValue = fieldString.ToString().TrimEnd(',');
            String paraStringValue = paraString.ToString().TrimEnd(',');
            
            String sql = String.Format(entitySqlTemplate, tableName, fieldStringValue, paraStringValue,  PrimaryKey);
            return sql;
        }

        public String GetInsertSqlWithoutParameter()
        {
            const String entitySqlTemplate = @" Insert into {0}({1})
                                                Values( {2} )";
            String tableName = this.GetType().Name;

            StringBuilder fieldString = new StringBuilder();
            
            StringBuilder paraString = new StringBuilder();
            List<FieldInfo> selectedFileds = this.GetEntityInsertFields();
            foreach (FieldInfo fieldInfo in selectedFileds)
            {
                String fieldName = fieldInfo.Name;
                fieldString.Append(fieldName);
                fieldString.Append(",");

                Object fObj = fieldInfo.GetValue(this);
                if (fObj == null)
                    fObj = "";
                SqlDbType dbType= SqlUtils.GetSqlDataType(fieldInfo.GetType(), 10);
                paraString.Append(SqlUtils.CreateSqlValue(fObj.ToString(),dbType));
                paraString.Append(",");
            }
            String fieldStringValue = fieldString.ToString().TrimEnd(',');
            String paraStringValue = paraString.ToString().TrimEnd(',');

            String sql = String.Format(entitySqlTemplate, tableName, fieldStringValue, paraStringValue, PrimaryKey);
            return sql;
        }
        
        public String GetUpdateSqlWithoutParameter(bool single)
        {
            const String entitySqlTemplate = @" Update {0}
                                                Set {1}
                                                Where {2}
                                              ";

            String tableName = this.GetType().Name;
            StringBuilder updateString = new StringBuilder();
            List<FieldInfo> selectedFileds = this.GetEntityUpdateFields();

            foreach (FieldInfo fieldInfo in selectedFileds)
            {
                String fieldName = fieldInfo.Name;

                Object fObj = fieldInfo.GetValue(this);
                if (fObj == null)
                    fObj = "";
                SqlDbType dbType = SqlUtils.GetSqlDataType(fieldInfo.GetType(), 10);
                updateString.AppendFormat("{0}={1},", fieldName,SqlUtils.CreateSqlValue(fObj.ToString(), dbType));
            }
            String updateSql = updateString.ToString().TrimEnd(',');
            String identitySql = GetIndetitySqlWithoutParameter();

            return String.Format(entitySqlTemplate, tableName, updateSql, identitySql);
        }

        private String GetUpdateSql(bool single)
        {
            const String entitySqlTemplate = @" Update {0}
                                                Set {1}
                                                Where {2}

                                                Select @{3}
                                              ";
                                                 
            String tableName = this.GetType().Name;
            StringBuilder updateString = new StringBuilder();
            List<FieldInfo> selectedFileds = this.GetEntityUpdateFields();
             
            foreach (FieldInfo fieldInfo in selectedFileds)
            {
                String fieldName = fieldInfo.Name;
                updateString.AppendFormat("{0}=@{0},", fieldName);
            }
            String updateSql = updateString.ToString().TrimEnd(',');
            String identitySql = GetIndetitySql();

            return String.Format(entitySqlTemplate, tableName, updateSql, identitySql, single?this.PrimaryKey:"EID"); 
        }

        public String GetDeleteSql()
        {
            String key = this.GetType().FullName;
            if (sqlConfig.EntitySqlConfigCollection.ContainsKey(key) &&
                !String.IsNullOrEmpty(sqlConfig.EntitySqlConfigCollection[key].DeleteSql.Trim()))
            {
                return sqlConfig.EntitySqlConfigCollection[key].DeleteSql;
            }

            String tableName = this.GetType().Name;
            return String.Format("Delete From {0} Where {1}=@{1}", tableName,this.PrimaryKey);
        }

        public void SelectSelf(SqlClient webClient)
        {
            SelectSelf(this.PrimaryKey + "=@" + PrimaryKey.ToUpper(), webClient, PrimaryKey);
        }

        /// <summary>
        /// 选择自己
        /// </summary>
        /// <param name="condition">不要加Where,如：ID=@ID</param>
        /// <param name="webClient"></param>
        public void SelectSelf(String condition,SqlClient webClient,params String[] paraFields)
        {
            String selectF = this.GetSelectString();
            if (String.IsNullOrEmpty(condition))
                condition =String.Format("{0}=@{0}",this.PrimaryKey);
            String sql = String.Format("Select {2} from {0} Where {1}", this.GetType().Name, condition, selectF);
            DbParameter[] parameters = this.GetEntitySqlParameter(paraFields);
            using (DbDataReader reader = webClient.ExecuteReader(new SqlStatemate(sql,parameters)))
            {
                while (reader.Read())
                {
                    this.MapEntity(reader);
                }
            }
        }
       
        private String GetSelectString()
        {
            StringBuilder selectString = new StringBuilder();
            List<FieldInfo> selectedFileds = this.GetEntitySelectedFields();
            int count =0;
            foreach (FieldInfo fieldInfo in selectedFileds)
            {
                String fieldName = fieldInfo.Name;
                if (count == 0)
                {
                    selectString.AppendFormat("{0}", fieldName);
                }
                else
                {
                    selectString.AppendFormat(",{0}", fieldName);
                }
                count++;
            }
            if (String.IsNullOrEmpty(selectString.ToString()))
                return " * ";
            return selectString.ToString();
        }

        public static String NewId()
        {
            return Guid.NewGuid().ToString().Replace("-", "").ToUpper();
        }

        public void Save(SqlClient webClient,params String[] fields)
        {
            String sql = GetSaveSql(true,true);
            SqlStatemate state = new SqlStatemate(sql, this.GetEntitySqlParameter(fields));
            String newId = webClient.GetSingleResult(state);
            this.ID = newId;
        }

        public void Save(OLEClient webClient)
        {
            String tableName = this.GetType().Name;
            String sql = String.Format("Select ID From {0} Where {1}",tableName,GetIndetitySqlWithoutParameter());
            String idStr = webClient.GetSingleResult(sql);
            if (String.IsNullOrEmpty(idStr))
            {
                sql =GetInsertSqlWithoutParameter();
            }
            else
            {
                this.ID = idStr;
                sql = GetUpdateSqlWithoutParameter(false);
            }
            webClient.ExecuteNoResult(sql);
        }

        public void Update(OLEClient webClient)
        {
            String sql = this.GetUpdateSqlWithoutParameter(false);
            webClient.ExecuteNoResult(sql);
        }

        public void Update(SqlClient webClient, params String[] fields)
        {
            String sql = this.GetUpdateSql(true);
            SqlStatemate state = new SqlStatemate(sql, this.GetEntitySqlParameter(fields));
            String newId = webClient.GetSingleResult(state);
            this.ID = newId;
        }

        public void InsertDotCheckExists(SqlClient webClient, params String[] fields)
        {
            String sql = this.GetInsertSql();
            SqlStatemate state = new SqlStatemate(sql, this.GetEntitySqlParameter(fields));
            String newId = webClient.GetSingleResult(state);
            this.ID = newId;
        }

        public void InsertCheckExists(SqlClient webClient, params String[] fields)
        {
            String sql = this.GetSaveSql(true,false);
            SqlStatemate state = new SqlStatemate(sql, this.GetEntitySqlParameter(fields));
            String newId = webClient.GetSingleResult(state);
            this.ID = newId;
        }

        public void Remove(SqlClient sqlClient)
        {
            this.selectFields.Add(this.PrimaryKey);
            String sql = this.GetDeleteSql();
            SqlStatemate sqlState = new SqlStatemate(sql, this.GetEntitySqlParameter(this.PrimaryKey));
            sqlClient.ExecuteNoResult(sqlState);
            this.selectFields.Clear();
        }
        public void Remove(OLEClient sqlClient)
        {
            String tableName = this.GetType().Name;
            String sql =String.Format("Delete From {0} Where {1}",tableName,GetIndetitySqlWithoutParameter());
            sqlClient.ExecuteNoResult(sql);
        }

        public List<T> Select<T>(String condition,SqlClient webSqlClient, params String[] fields) where T : BaseEntity, new()
        {
            return this.Select<T>(condition, "", webSqlClient, fields);  
        }
        public List<T> Select<T>(String condition,String SortSql, SqlClient webSqlClient, params String[] fields) where T : BaseEntity, new()
        {
            StringBuilder selectString = new StringBuilder();
            selectString.Append("Select ");
            String selectF = this.GetSelectString();
            selectString.Append(selectF);
            selectString.AppendFormat(" From {0} ", this.GetType().Name);
            if (!String.IsNullOrEmpty(condition))
            {
                selectString.AppendFormat(" Where {0} ", condition);
            }
            if (!String.IsNullOrEmpty(SortSql))
            {
                selectString.AppendFormat(" Order by {0} ", SortSql);
            }
            using (SqlExecBlock<T> execBlock = new SqlExecBlock<T>(selectString.ToString(), SqlType.Text, webSqlClient))
            {
                execBlock.Parameters.AddRange(this.GetEntitySqlParameter(fields));
                return execBlock.ExecuteReturnEntityList();
            }
        }
        
        public static BaseEntity GetEntityByName(String entityName)
        {
            if (entityTypes == null || entityTypes.Length == 0)
                throw new Exception("Can not find object.");
            foreach (Type entityType in entityTypes)
            {
                if (String.Compare(entityType.Name, entityName, true) == 0 &&
                    (entityType.BaseType == typeof(BaseEntity) || 
                    entityType.BaseType.BaseType == typeof(BaseEntity)))
                {
                    Object instance = Activator.CreateInstance(entityType);
                    return (BaseEntity)instance;
                }
            }
            throw new Exception("Can not find the object in database:"+entityName);
        }

        public void CleanSelectInfo()
        {
            this.selectFields.Clear();
            this.unInsertField.Clear();
            this.unUpdateFields.Clear();
        }

        public static void CleanCache(String catchKey)
        {
            if (!String.IsNullOrEmpty(catchKey) && selectFieldCache.ContainsKey(catchKey))
            {
                selectFieldCache.Remove(catchKey);
            }
            if (!String.IsNullOrEmpty(catchKey) && mapFieldColCache.ContainsKey(catchKey))
            {
                mapFieldColCache.Remove(catchKey);
            }
        }

        public void CleanCache()
        {
            DataLayerCache.Remove(this.ID);
        }

        public String ToXml()
        {
            List<FieldInfo> entityFields = this.GetEntityTableFileds(false); ;
             
            XmlDocument doc = new XmlDocument();
            XmlNode root = doc.CreateElement("entity");

            String key = this.GetType().FullName;
            ///////////////////////////////////////////////////////////
            XmlAttribute classNameAttr = doc.CreateAttribute("type");
            classNameAttr.Value = key;
            root.Attributes.Append(classNameAttr);
 
            XmlAttribute assAttr = doc.CreateAttribute("assmbly");
            assAttr.Value = this.GetType().Assembly.FullName;
            root.Attributes.Append(assAttr);

            foreach (FieldInfo fieldInfo in entityFields)
            {
                XmlNode fieldNode = doc.CreateElement("field");

                ///////////////////////////////////////////////////////////
                XmlAttribute fieldNameAttr = doc.CreateAttribute("name");
                fieldNameAttr.Value = fieldInfo.Name;
                fieldNode.Attributes.Append(fieldNameAttr);

                ///////////////////////////////////////////////////////////
                fieldNode.InnerText = GetFieldValueToString(fieldInfo); ;
                root.AppendChild(fieldNode);
            }
            doc.AppendChild(root);
            return "<?xml version=\"1.0\" encoding=\"gbk\"?> \r\n" + doc.OuterXml;
        }

        public void FromXml(String xml)
        {
            if (String.IsNullOrEmpty(xml))
                return;
            using (XmlTextReader reader = new XmlTextReader(xml, XmlNodeType.Element, null))
            {
                reader.MoveToContent();
                do
                {
                    if (String.Compare("entity", reader.Name, true) == 0)
                    {
                        String type = reader.GetAttribute("type");
                        if (String.IsNullOrEmpty(type))
                            continue;
                        if (String.Compare(type, this.GetType().FullName) != 0)
                        {
                            throw new Exception("类型不匹配:" + type);
                        }
                    }
                    if (String.Compare("field", reader.Name, true) == 0)
                    {
                        String fieldName = reader.GetAttribute("name");
                        String fieldValue = reader.ReadString();
                        FieldInfo fieldInfo = this.GetType().GetField(fieldName, BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
                        if (fieldInfo != null)
                        {
                            StringToFieldInfo(fieldInfo, fieldValue, this);
                        }
                    }
                } while (reader.Read());
            }
        }
        
        public static String EntityArrayToXml<T>(List<T> entities) where T : BaseEntity, new()
        {
            StringBuilder xml = new StringBuilder();
            xml.Append("<entities>").AppendLine();
            foreach (T entity in entities)
            {
                xml.Append(entity.ToXml()).AppendLine();
            }
            xml.Append("</entities>").AppendLine();
            return xml.ToString();
        }
       
        private String GetFieldValueToString(FieldInfo fieldInfo)
        {
            if ( fieldInfo.FieldType == typeof(Int32) ||
                 fieldInfo.FieldType == typeof(Int64) ||
                 fieldInfo.FieldType == typeof(Int16) ||
                 fieldInfo.FieldType == typeof(long)||
                 fieldInfo.FieldType == typeof(String))
            {
                Object orgValue = fieldInfo.GetValue(this);
                if (orgValue == null)
                    return "";
                return orgValue.ToString();
            }
            else if (fieldInfo.FieldType == typeof(byte[]))
            {
                byte[] data = fieldInfo.GetValue(this) as byte[];
                if (data == null)
                    return "";
                return Convert.ToBase64String(data);
            }
            return fieldInfo.GetValue(this) == null ? "" : fieldInfo.GetValue(this).ToString();
        }

        public static List<BaseEntity> LoadFromXml (String xml)  
        {
            List<BaseEntity> results = new List<BaseEntity>();
            if (String.IsNullOrEmpty(xml))
                return results;
            using (XmlTextReader reader = new XmlTextReader(xml, XmlNodeType.Element, null))
            {
                reader.MoveToContent();
                do
                {
                    if (String.Compare("entity", reader.Name, true) == 0)
                    {
                       BaseEntity result= LoadFromXml(reader.ReadSubtree());
                       if (result != null)
                       {
                           results.Add(result);
                       }
                    }
                } while (reader.Read());
            }
            return results;
        }

        public static BaseEntity LoadFromXml(XmlReader reader)
        {
            Object instance=null;
            Type instanceType = null;
            do
            {
                if (String.Compare("entity", reader.Name, true) == 0)
                {
                    String type = reader.GetAttribute("type");
                    if (String.IsNullOrEmpty(type))
                        continue;
                    String assmblyName = reader.GetAttribute("assmbly");
                    if (entityTypeCache.ContainsKey(type))
                    {
                        instanceType = entityTypeCache[type];
                    }
                    instance = Activator.CreateInstance(instanceType);
                }
                if (String.Compare("field", reader.Name, true) == 0 && instance != null)
                {
                    String fieldName = reader.GetAttribute("name");
                    String fieldValue= reader.ReadString();
                    FieldInfo fieldInfo = instanceType.GetField(fieldName, BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
                    if (fieldInfo != null)
                    {
                        StringToFieldInfo(fieldInfo, fieldValue, instance);
                    }
                }

            } while (reader.Read());
            return instance as BaseEntity;
        }

        private static void StringToFieldInfo(FieldInfo fieldInfo, String fValue,Object instance)
        {
            if (fieldInfo.FieldType == typeof(String))
            {
                fieldInfo.SetValue(instance, fValue);
            }
            else if (fieldInfo.FieldType == typeof(Int32) ||
                     fieldInfo.FieldType == typeof(Int64) ||
                     fieldInfo.FieldType == typeof(Int16) ||
                     fieldInfo.FieldType == typeof(long))
            {
                int intValue;
                int.TryParse(fValue.Trim(), out intValue);
                fieldInfo.SetValue(instance, intValue);
            }
            else if (fieldInfo.FieldType == typeof(byte[]))
            {
                byte[] data = Convert.FromBase64String(fValue);
                fieldInfo.SetValue(instance, data);
            }
            else if (fieldInfo.FieldType == typeof(decimal) || fieldInfo.FieldType == typeof(Decimal))
            {
                decimal data;
                decimal.TryParse(fValue, out data);
                fieldInfo.SetValue(instance, data);
            }
            else
            {
                fieldInfo.SetValue(instance, fValue);
            }
        }

        private class PrimaryKeyEntity
        {
            public String PrimaryKey = "ID";
            public String PrimaryKeyType = " varchar(66)";
            public bool IDAutoIncrease = false;
        }

        public virtual String ToJSON()
        {
            StringBuilder json = new StringBuilder();
            List<FieldInfo> entityFields = GetEntityTableFileds(true);
            int i = 0;
            foreach (FieldInfo field in entityFields)
            {
                object objValue = field.GetValue(this);
                if (field.FieldType == typeof(int) ||
                    field.FieldType == typeof(float) ||
                    field.FieldType == typeof(double) ||
                    field.FieldType == typeof(decimal) ||
                    field.FieldType == typeof(bool) ||
                    field.FieldType == typeof(uint) ||
                    field.FieldType == typeof(ulong))
                {
                    json.AppendFormat("'{0}':{1} ", field.Name.ToLower(), objValue == null ? "null" : objValue.ToString());
                }
                else
                {
                    json.AppendFormat("'{0}':{1} ", field.Name.ToLower(), objValue == null ? "null" : "\"" + EncodeUtils.EncodeJson(objValue.ToString()) + "\"");
                }
                if (i < entityFields.Count-1)
                {
                    json.Append(",");
                }
                i++;
            }
            return "{" + json.ToString() +"}";
        }
    }
   
}
